<?php
/**
* Cleans XHTML code from unallowed tags and attributes
* 
* @author Dmitry Polushkin <dmitry.polushkin@gmail.com>
* @example
* <code>
* require('class.xhtmlscrubber.php');
* $xhtmlscrubber = new XHTMLScrubber;
* print_r($xhtmlscrubber->clean('<a href="/link/">Link</a> <script></script> <img src="/image.gif" alt="Image." /> <b>And <i>text</i></b> + <iframe></iframe>'));
* </code>
* @copyright http://creativecommons.org/licenses/by-nc-nd/2.5/
* @version 0.1
*/
class XHTMLScrubber {
	private $allowed = array('i'=>null, 'br'=>null, 'b'=>null, 'img'=>array('src', 'alt'), 'a'=>array('href', 'title'));
	private $matches = null;
	
	public function __construct($allowed = null) {
		if (!is_null($allowed)) {
			$this->allowed = $allowed;
		}
		
		$this->matches = join('|', array_keys($this->allowed));
	}

	public function clean($string) {
		$tags_new = array();
		$tags_old = array();
		$attributes = array();
		
		preg_match_all('@<(?:(?:('.$this->matches.')(?:[ /](?:[^>]*)>|>))|(?:/('.$this->matches.')>))@i', $string, $tags_old, PREG_SET_ORDER);

		foreach ($tags_old as $i=>$tag) {
			$type = 1;
			if (isset($tag[2])) {
				$type = 2;
			}
			$tags_new[$i] = array('name'=>strtolower($tag[$type]), 'attributes'=>array());
			$tags_new[$i]['single'] = (substr($tag[0], -2, 1)=='/'?true:false);
			$tags_new[$i]['end'] = (substr($tag[0], 1, 1)=='/'?true:false);
			
			preg_match_all('@([\w]+)=["\']([^"\'>]*)["\']@i', $tag[0], $attributes, PREG_SET_ORDER);
			if (is_array($this->allowed[$tags_new[$i]['name']]) && !empty($attributes)) {
				foreach ($attributes as $attribute) {
					if (in_array($attribute[1], $this->allowed[$tags_new[$i]['name']])) {
						$tags_new[$i]['attributes'][] = array('name'=>$attribute[1], 'value'=>$attribute[2]);
					}
				}
			}
		}
		
		$string = str_replace(array('<', '>'), array('&lt;', '&gt;'), $string);
		
		foreach ($tags_old as $i=>$tag) {
			$to = '<'.($tags_new[$i]['end']?'/':'').$tags_new[$i]['name'];
			foreach ($tags_new[$i]['attributes'] as $attribute) {
				$to .= ' '.$attribute['name'].'="'.$attribute['value'].'"';
			}
			$to .= ($tags_new[$i]['single']?' /':'').'>';
			$string = str_ireplace(str_replace(array('<', '>'), array('&lt;', '&gt;'), $tag[0]), $to, $string);
		}
		
		return $string;
	}
}
?>